package dubbot

import (
	dubboconfig "dubbo.apache.org/dubbo-go/v3/config"
	hessian "github.com/apache/dubbo-go-hessian2"
	"github.com/hyperway/pkg/component"
	"github.com/hyperway/pkg/constant"
	"github.com/hyperway/pkg/logger"
	"sync"
)

// dubbo-go必须的初始化
import (
	_ "dubbo.apache.org/dubbo-go/v3/imports"
)

func init() {
	// 加载默认配置，OnInit方法覆盖
	dubboconfig.Load()
}

var tOnce sync.Once

var instance *DubboTransporter

type (
	// DubboTransporter Dubbo转发器
	DubboTransporter struct {
		*DubboInvoker
	}
)

func GetDubboInvoker() *DubboInvoker {
	if instance == nil {
		// DubboTransporter未初始化
		panic("DubboTransporter未初始化")
	}
	return instance.DubboInvoker
}

func newTransporter() *DubboTransporter {
	tOnce.Do(func() {
		instance = &DubboTransporter{
			DubboInvoker: &DubboInvoker{
				lock: &sync.Mutex{},
			},
		}
	})
	return instance
}

func (d *DubboTransporter) Name() string {
	return constant.Dubbo
}

func (d *DubboTransporter) DoInvoke(ctx component.InnerContext) (*component.ServerResponse, *component.ServerError) {
	trace := logger.Trace(ctx.Context())

	types, values, err := d.resolveArguments(ctx)
	if err != nil {
		trace.Errorw("[dubbo参数解析失败]", "error", err)
		return nil, &component.ServerError{
			StatusCode: 500,
			Message:    "dubbo参数解析失败",
			ErrorCode:  "DUBBO_PARAM_PARSE_ERROR",
		}
	}

	attachments, err := d.resolveAttachments(ctx)
	if err != nil {
		trace.Errorw("[dubbo附件解析失败]", "error", err)
		return nil, &component.ServerError{
			StatusCode: 500,
			Message:    "dubbo附件解析失败",
			ErrorCode:  "DUBBO_ATTACHMENT_PARSE_ERROR",
		}
	}

	dubboContext, err := NewContext(
		ctx.Context(),
		ctx.Service().Interface,
		ctx.Service().Method,
		d.getGroup(ctx),
		d.getVersion(ctx),
		ctx.Service().Protocol,
		types,
		values,
		attachments,
		trace,
	)

	if err != nil {
		trace.Errorw("[dubbo上下文构建失败]", "error", err)
		return nil, &component.ServerError{
			StatusCode: 500,
			Message:    "dubbo上下文构建失败",
			ErrorCode:  "DUBBO_CONTEXT_BUILD_ERROR",
		}
	}

	ret, e := d.Invoke(dubboContext)

	return ret, e
}

func (d *DubboTransporter) resolveArguments(ctx component.InnerContext) ([]string, interface{}, error) {
	arguments := ctx.Service().Arguments
	argSize := len(arguments)
	types := make([]string, argSize)
	values := make([]hessian.Object, argSize)
	var e error
	for i, argument := range arguments {
		types[i] = argument.ClassType
		values[i], e = GetDubboValue(ctx, argument)
		if e != nil {
			return nil, nil, e
		}
	}
	return types, values, e
}

func (d *DubboTransporter) resolveAttachments(ctx component.InnerContext) (map[string]interface{}, error) {
	return ctx.Attributes(), nil
}

func (d *DubboTransporter) getGroup(ctx component.InnerContext) string {
	return ctx.Header(constant.XRequestGroup)
}

func (d *DubboTransporter) getVersion(ctx component.InnerContext) string {
	return ctx.Header(constant.XRequestVersion)
}

func (d *DubboTransporter) GetDubboInvoker() *DubboInvoker {
	return d.DubboInvoker
}
